#pragma once

#include <array>
#include <atomic>
#include <cmath>
#include <cstddef>
#include <semaphore>
#include <thread>

#include "common_affinity.hpp"

/**
 * @brief 单线程矩阵乘法 + sqrt 混合运算，用于观察缓存稳定性。
 */
inline double do_work() {
  constexpr std::size_t kMatrixSize = 64;

  std::array<double, kMatrixSize * kMatrixSize> a{};
  std::array<double, kMatrixSize * kMatrixSize> b{};
  std::array<double, kMatrixSize * kMatrixSize> c{};

  for (std::size_t i = 0; i < a.size(); ++i) {
    a[i] = static_cast<double>(i % 97);
    b[i] = static_cast<double>((i * 7) % 101);
  }

  for (std::size_t row = 0; row < kMatrixSize; ++row) {
    for (std::size_t col = 0; col < kMatrixSize; ++col) {
      double accum = 0.0;
      for (std::size_t k = 0; k < kMatrixSize; ++k) {
        accum += a[row * kMatrixSize + k] * b[k * kMatrixSize + col];
      }
      c[row * kMatrixSize + col] = std::sqrt(accum + 1.0);
    }
  }

  double checksum = 0.0;
  for (double v : c) {
    checksum += v;
  }
  return checksum;
}

/**
 * @brief 每轮线程切换次数（Ping-Pong）。
 */
inline constexpr std::size_t kSwitchesPerIteration = 10'000;

/**
 * @brief 使用二进制信号量实现的线程 Ping-Pong。
 * @tparam PinThreads 为 true 时，两个线程分别绑定到 core0、core1。
 */
template <bool PinThreads>
inline void run_thread_switch_ping_pong() {
  std::binary_semaphore sem_main{0};
  std::binary_semaphore sem_worker{0};
  std::atomic<bool> stop_flag{false};

  std::thread worker([&]() {
    if constexpr (PinThreads) {
      if (!Common::setThreadCore(1)) {
        std::cerr << "[Pinned PingPong] 工作者线程绑定 core1 失败，将当作未绑定处理\n";
      }
    }
    while (true) {
      sem_worker.acquire();
      if (stop_flag.load(std::memory_order_acquire)) {
        break;
      }
      sem_main.release();
    }
  });

  if constexpr (PinThreads) {
    if (!Common::setThreadCore(0)) {
      std::cerr << "[Pinned PingPong] 主线程绑定 core0 失败，将当作未绑定处理\n";
    }
  }

  for (std::size_t i = 0; i < kSwitchesPerIteration; ++i) {
    sem_worker.release();
    sem_main.acquire();
  }

  stop_flag.store(true, std::memory_order_release);
  sem_worker.release();
  worker.join();
}